/*
 * Stok3d - getting you stoked on Flash Player native 3D
 * Copyright (C) 2009  David Lenaerts
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * Author information:
 * David Lenaerts
 * url: http://www.derschmale.com
 * mail: david.lenaerts@gmail.com
 * 
 */

package com.derschmale.stok3d.filters
{
	import flash.display.BitmapData;
	import flash.display.DisplayObject;
	import flash.display.Shader;
	import flash.geom.Vector3D;
	

	/**
	 * The EnvMapPhongFilter class creates a combination of cubic environment mapping and phong shading
	 * on a DisplayObject.
	 * 
	 * @author David Lenaerts
	 * http://www.derschmale.com
	 */
	public class EnvMapPhongFilter extends PixelShader3D
	{
		[Embed(source="/../pbj/EnvMapPhongNormalShader.pbj", mimeType="application/octet-stream")]
		private var NormalMapKernel : Class;
		
		[Embed(source="/../pbj/EnvMapPhongFlatShader.pbj", mimeType="application/octet-stream")]
		private var FlatKernel : Class;
		
		[Embed(source="/../pbj/EnvMapPhongSpecularShader.pbj", mimeType="application/octet-stream")]
		private var SpecularMapKernel : Class;
		
		private var _faces : Array;
		private var _alpha : Number;
		private var _normalMapStrength : Number;
		private var _specular : Number;
		private var _gloss : Number;
		private var _ambientColor : uint;
		private var _specularColor : uint;
		private var _lightPosition : Vector3D;
		private var _hasNormalMap : Boolean;
		private var _hasSpecularMap : Boolean;
		
		/**
		 * Create an EnvMapFilter instance.
		 * 
		 * @param faces An key-value array of BitmapData objects. Use the constants LEFT, RIGHT, TOP, BOTTOM, BACK, FRONT as keys.
		 * @param alpha The opacity of the environment map, ie: how reflective the surface is. 1 is a perfect mirror.
		 * @param normalMap A normal map in tangent space. If not provided, the surface will appear flat.
		 * @param normalMapStrength Indicates how strong the normal map affects the surface relief.
		 * 
		 * @see com.derschmale.stok3d.filters.EnvMapFaces
		 */
		public function EnvMapPhongFilter(faces : Array, alpha : Number = 0.5, specular : Number = 1, gloss : Number = 20, normalMap : BitmapData = null, normalMapStrength : Number = 0.5, specularMap : BitmapData = null)
		{
			var shader : Shader;
			_hasNormalMap = normalMap != null;
			
			if (normalMap) {
				_hasNormalMap = true;
				
				if (specularMap) {
					_hasSpecularMap = true;
					shader = new Shader(new SpecularMapKernel());
					shader.data.specularMap.input = specularMap;
				}
				else shader = new Shader(new NormalMapKernel());
				
				shader.data.normalMap.input = normalMap;
				shader.data.normalMapStrength.value = [ normalMapStrength ];
			}
			else shader = new Shader(new FlatKernel());
			
			super(shader);

			_alpha = alpha;
			_faces = faces;
			_normalMapStrength = normalMapStrength;
			shader.data.left.input = faces[EnvMapFaces.LEFT];
			shader.data.right.input = faces[EnvMapFaces.RIGHT];
			shader.data.top.input = faces[EnvMapFaces.TOP];
			shader.data.bottom.input = faces[EnvMapFaces.BOTTOM];
			shader.data.front.input = faces[EnvMapFaces.FRONT];
			shader.data.back.input = faces[EnvMapFaces.BACK];
			shader.data.cubeDim.value = [ faces[EnvMapFaces.LEFT].width*.5 ];
			shader.data.alpha.value = [ alpha ];
			
			this.specular = specular;
			this.gloss = gloss;
			
			lightPosition = new Vector3D();
		}
		
		/**
		 * The global (minimum) illumination colour.
		 */
		public function get ambientColor() : uint
		{
			return _ambientColor;
		}
		
		public function set ambientColor(value : uint) : void
		{
			_ambientColor = value;
			shader.data.ambientColor.value = [ 	(value >> 16)/0xff,
												((value >> 8) & 0xff)/0xff,
												(value & 0xff)/0xff
											]; 
		}
		
		/**
		 * The colour of the specular highlight.
		 */
		public function get specularColor() : uint
		{
			return _specularColor;
		}
		
		public function set specularColor(value : uint) : void
		{
			_specularColor = value;
			shader.data.specularColor.value = [ 	(value >> 16)/0xff,
													((value >> 8) & 0xff)/0xff,
													(value & 0xff)/0xff
												]; 
		}
		
		/**
		 * The position of the light in 3D space
		 */
		public function get lightPosition() : Vector3D
		{
			return _lightPosition;
		}
		
		public function set lightPosition(value : Vector3D) : void
		{
			_lightPosition = value;
		}
		
		/**
		 * The strength of the specular highlight
		 */
		public function get specular() : Number
		{
			return _specular;
		}
		
		public function set specular(value : Number) : void
		{
			_specular = value;
			shader.data.specular.value = [ value ];
		}
		
		/**
		 * The gloss component of the specular highlight. Higher numbers will result in a smaller and sharper highlight.
		 */
		public function get gloss() : Number
		{
			return _gloss;
		}
		
		public function set gloss(value : Number) : void
		{
			_gloss = value;
			shader.data.gloss.value = [ value ];
		}
		
		/**
		 * The opacity of the environment map, ie: how reflective the surface is. 1 is a perfect mirror.
		 */
		public function get alpha() : Number
		{
			return _alpha;
		}
		
		public function set alpha(value : Number) : void
		{
			_alpha = value;
			shader.data.alpha.value = [ value ];
		}
		
		/**
		 * Indicates how strong the normal map affects the surface relief.
		 */
		public function get normalMapStrength() : Number
		{
			return _normalMapStrength;
		}
		
		public function set normalMapStrength(value : Number) : void
		{
			if (_hasNormalMap) {
				_normalMapStrength = value;
				shader.data.normalMapStrength.value = [ value ];
			}
		}
		
		/**
		 * Updates the filter. This function needs to be called whenever the target displayObject changes position, rotation or scale.
		 * 
		 * @param target The DisplayObject for which the shader needs to be updated.
		 */
		override public function update(target : DisplayObject) : void
		{
			super.update(target);
			shader.data.lightPosition.value = [ _lightPosition.x, _lightPosition.y, _lightPosition.z ];
		}
	}
}